node.js express boilerplate

node.js express 프로젝트를 시작할 때 주로 사용되는 패키지들을 정리한 bilerplate

express setup

install

npm install express express-session
npm install --save-dev nodemon
npm install winston winston-daily-rotate-file morgan
npm install cookie-parser
npm install cors

cors

CORS를 위한 node cors패키지 사용

코드

const cors = require("cors");
const corsOptions = require("./config/corsOptions");
const allowedOrigins = require("./allowedOrigins");

const corsOptions = {
  origin: (origin, callback) => {
    if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
      callback(null, true);
    } else {
      callback(new Error("Not allowed by CORS"));
    }
  },
  credentials: true,
  optionsSuccessStatus: 200,
};

module.exports = corsOptions;

cors allow 목록을 별도로 관리

const allowedOrigins = ["http://localhost:3000", "http://localhost:4000"];
module.exports = allowedOrigins;

모든 credentail을 지정할 것 이 아니라면 별도의 미들웨어를 만들어서 헤더에 Acces-Control-Credentials 을 true로 지정해주면된다.

const allowedOrigins = require("../../config/allowedOrigins");

const credentails = (req, res, next) => {
  const origin = req.headers.origin;
  console.log(origin);
  if (allowedOrigins.includes(origin)) {
    console.log("allowed");
    res.header("Access-Control-Allow-Credentails", true);
  }
  next();
};

module.exports = credentails;

parser를 위한 패키지

express parser

express#express.json( [options])
express#express.urlencoded( [options])
express내장 json과 urlencode parser

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

cookieParser
cookie 파싱을 위한 패키지

const cookieParser = require("cookie-parser");
app.use(cookieParser(process.env.COOKIE_SECRET));

session

express-session
세션 관리 패키지

const session = require("express-session");
app.use(
  session({
    secret: process.env.COOKIE_SECRET,
    resave: false,
    saveUninitialized: false,
    store: new RedisStore({
      client: redisClient,
    }),
    //cookie: { secure: true },
  })
);

logger

nodejs winstonnodejs morgan 을 이용해서 Logging 시스템을 구축했다.
winston은 log를 다루는 패키지이고 morgan은 http 요청에 대한 logging한다.
즉, winston으로 Logger를 만들고 morgan middleware는 요청 log를 logger에 싣는다.

const logger = require("./utils/logger");
const morganMiddleware = require("./utils/middleware/morgan");
const winston = require("winston");
const winstonDaily = require("winston-daily-rotate-file");
// const appRoot = require("app-root-path");
const process = require("process");

const logDir = `${process.cwd()}/logs`;

const { combine, timestamp, printf, label } = winston.format;

const logFormat = printf(({ level, message, label, timestamp }) => {
  return `${timestamp} [${label}] ${level}: ${message}`;
});

const logger = winston.createLogger({
  format: combine(
    label({ label: "express" }),
    timestamp({
      format: "YYYY-MM-DD HH:mm:ss",
    }),
    logFormat
  ),
  transports: [
    new winstonDaily({
      level: "debug",
      datePattern: "YYYY-MM-DD",
      dirname: logDir,
      filename: `%DATE%.log`,
      maxFiles: 30,
      zippedArchive: true,
    }),
    new winstonDaily({
      level: "error",
      datePattern: "YYYY-MM-DD",
      dirname: logDir + "/error",
      filename: `%DATE%.error.log`,
      maxFiles: 30,
      zippedArchive: true,
    }),
  ],
  exceptionHandlers: [
    new winstonDaily({
      level: "error",
      datePattern: "YYYY-MM-DD",
      dirname: logDir,
      filename: `%DATE%.log`,
      maxFiles: 30,
      zippedArchive: true,
    }),
  ],
});

if (process.env.NODE_ENV !== "production") {
  logger.add(
    new winston.transports.Console({
      format: winston.format.combine(
        process.env.NODE_ENV === "development"
          ? winston.format.colorize()
          : winston.format.uncolorize(),
        winston.format.simple() // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷으로 출력
      ),
    })
  );
}

module.exports = logger;

log를 날짜별 파일에 남기기 위해 winston-dailly-rotate-file 패키지를 사용했다.
logging할 때 만든 logger를 사용한다.

const morgan = require("morgan");
const logger = require("../logger");

const format = () => {
  const morganFormat =
    process.env.NODE_ENV === "production" ? "combined" : "dev";
  return morganFormat;
};

const stream = {
  write: (message) =>
    logger.info(
      message.replace(
        /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
        ""
      )
    ),
};

const morganMiddleware = morgan(format(), { stream });

module.exports = morganMiddleware;

전체 코드

// import packages
const express = require("express");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const session = require("express-session");

// import config
const corsOptions = require("./config/corsOptions");
const sessionOptions = require("./config/sessionOptions");

// import utils
const logger = require("./utils/logger");
const morganMiddleware = require("./utils/middleware/morgan");

// express setup
const app = express();
app.use(cors(corsOptions));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser("cookie-secret"));
app.use(morganMiddleware);
app.use(session(sessionOptions));

// test route
app.use(require("./routes"));

const PORT = process.env.PORT || 3000;
app.listen(PORT, logger.info(`Server is running on port ${PORT}`));

github node_boilerplate